//
// Created by Crestimes on 2024/11/18.
//

#include <fstream>
#include <sstream>
#include "CodeAnalyzer.h"

#include "global.h"

CodeAnalyzer::CodeAnalyzer(
    const map<string, pair<map<size_t, map<string, size_t>>, set<size_t>>>& DFAsAndEndsOfTokenType,
    const map<string, pair<map<size_t, map<string, size_t>>, set<size_t>>>& DFAsAndEndsOfAuxiliary) {
    this->DFAsAndEndsOfTokenType = DFAsAndEndsOfTokenType;
    this->DFAsAndEndsOfAuxiliary = DFAsAndEndsOfAuxiliary;
}

/**
 * 对路径下文件内容进行词法分析，该静态方法的调用需要保证调用前当前目录下有对应的exe程序，该exe可以通过调用本类对象的generateCppExe函数生成
 * @param filePath 源代码路径
 * @return 源代码词法是否正确
 */
bool CodeAnalyzer::analyze(const string& filePath) {
    // return 0 正常执行，即正确，1则错误，所以要取反
    return !system((string(FILE_NAME) + ".exe \"" + filePath + "\"").c_str());
}

/**
 * 生成cpp文件和exe文件
 */
void CodeAnalyzer::generateCppExe() {
    // 取出拼接了所有正则值的正则表达式
    mainReDFA = DFAsAndEndsOfTokenType[CONCAT_RE_NAME].first;
    mainReEnds = DFAsAndEndsOfTokenType[CONCAT_RE_NAME].second;
    // 从映射中去除
    DFAsAndEndsOfTokenType.erase(CONCAT_RE_NAME);
    // 清空文件内容
    const string fileName = string(FILE_NAME) + ".cpp";
    ofstream(fileName).close();
    // 生成判断token类型的函数
    generateReFunctions();
    // 生成主函数
    generateMainFunction();
    // 生成exe
    system(("g++ -o ./" + string(FILE_NAME) + " " + fileName).c_str());
}

/**
 * 生成判断token类型的各个函数
 */
void CodeAnalyzer::generateReFunctions() const {
    ostringstream codeStream;
    string fileName = string(FILE_NAME) + ".cpp";
    ofstream file(fileName, ios::app);

    // 头文件
    codeStream << "#include <iostream>" << endl;
    codeStream << "#include <string>" << endl;
    codeStream << "#include <sstream>" << endl;
    codeStream << "#include <algorithm>" << endl;
    codeStream << "#include <vector>" << endl;
    codeStream << "#include <fstream>" << endl;
    codeStream << endl;
    codeStream << "using namespace std;" << endl;
    codeStream << endl;

    // 生成不带_的正则表达式的判断函数
    for (const auto& reDFAMap : DFAsAndEndsOfAuxiliary) {
        string reDFAName = reDFAMap.first;
        map<size_t, map<string, size_t>> reDFA = reDFAMap.second.first;
        set<size_t> reDFAEnds = reDFAMap.second.second;

        // 匹配该类型时返回值true，否则为false
        codeStream << "bool " + reDFAName + "(string token) {" << endl;
        codeStream << "    int currentState = 0;" << endl;
        codeStream << "    int length = token.length();" << endl;
        codeStream << "    for (int i = 0; i < length; i++) {" << endl;
        codeStream << "        string symbol(1, token[i]);" << endl;
        codeStream << "        switch (currentState) {" << endl;

        for (const auto& node : reDFA) {
            codeStream << "            case " << node.first << ":" << endl;
            codeStream << "                if (false) ;" << endl;
            for (const auto& toNodeMap : node.second) {
                if (toNodeMap.first == "\n") codeStream << R"(                else if (symbol == "\n") )";
                else codeStream << "                else if (symbol == \"" << toNodeMap.first << "\") ";
                codeStream << "currentState = " << toNodeMap.second << ";" << endl;
            }
            codeStream << "                else return false;" << endl;
            codeStream << "                break;" << endl;
        }

        codeStream << "            default:" << endl;
        codeStream << "                return false;" << endl;
        codeStream << "        }" << endl;
        codeStream << "    }" << endl;

        // 判断是否到达终态
        codeStream << "    switch (currentState) {" << endl;

        for (const auto& e : reDFAEnds) {
            codeStream << "        case " << e << ":" << endl;
            // codeStream << R"(            cout << "\tAccepted" << endl;)" << endl;
            codeStream << "            break;" << endl;
        }

        codeStream << "        default:" << endl;
        // codeStream << R"(            cout << "\tNot Accepted" << endl;)" << endl;
        codeStream << "            return false;" << endl;
        codeStream << "    }" << endl;
        codeStream << "    return true;" << endl;
        codeStream << "}" << endl << endl;

        cout << "Function [" + reDFAName + "] generated successfully." << endl;
    }

    for (const auto& reDFAMap : DFAsAndEndsOfTokenType) {
        string reDFAName = reDFAMap.first;
        map<size_t, map<string, size_t>> reDFA = reDFAMap.second.first;
        set<size_t> reDFAEnds = reDFAMap.second.second;

        // 匹配该类型时返回值为正则名，否则为宏定义的ILLEGAL
        codeStream << "string " + reDFAName + "(string token) {" << endl;
        codeStream << "    int currentState = 0;" << endl;
        codeStream << "    int length = token.length();" << endl;
        codeStream << "    for (int i = 0; i < length; i++) {" << endl;
        codeStream << "        char c = token[i];" << endl;
        codeStream << "        string symbol(1, token[i]);" << endl;
        codeStream << "        switch (currentState) {" << endl;

        for (const auto& node : reDFA) {
            codeStream << "            case " << node.first << ":" << endl;
            codeStream << "                if (false) ;" << endl;
            for (const auto& toNodeMap : node.second) {
                if (toNodeMap.first == "\n") codeStream << R"(                else if (symbol == "\n") )";
                else if (DFAsAndEndsOfAuxiliary.count(toNodeMap.first)) {
                    // 是不带_的正则名，则调用函数来判断是否符合该正则
                    codeStream << "                else if (" << toNodeMap.first << "(symbol)) ";
                }
                else codeStream << "                else if (symbol == \"" << toNodeMap.first << "\") ";
                codeStream << "currentState = " << toNodeMap.second << ";" << endl;
            }
            codeStream << "                else return \"" << ILLEGAL << "\";" << endl;
            codeStream << "                break;" << endl;
        }

        codeStream << "        default:" << endl;
        codeStream << "            return \"" << ILLEGAL << "\";" << endl;
        codeStream << "        }" << endl;
        codeStream << "    }" << endl;

        // 判断是否到达终态
        codeStream << "    switch (currentState) {" << endl;

        for (const auto& e : reDFAEnds) {
            codeStream << "        case " << e << ":" << endl;
            // codeStream << R"(            cout << "\tAccepted" << endl;)" << endl;
            codeStream << "            break;" << endl;
        }

        codeStream << "        default:" << endl;
        // codeStream << R"(            cout << "\tNot Accepted" << endl;)" << endl;
        codeStream << "            return \"" << ILLEGAL << "\";" << endl;
        codeStream << "    }" << endl;
        codeStream << "    return \"" << reDFAName << "\";" << endl;
        codeStream << "}" << endl << endl;

        cout << "Function [" + reDFAName + "] generated successfully." << endl;
    }

    // 写入文件
    if (file.is_open()) {
        file << codeStream.str();
        file.close();
    }
    else {
        cerr << "Failed to generate cpp file. Unable to open file: " << fileName << endl;
    }
    cout << endl;
}

/**
 * 生成分析程序的主程序入口函数
 */
void CodeAnalyzer::generateMainFunction() const {
    ostringstream codeStream;
    string fileName = string(FILE_NAME) + ".cpp";
    ofstream file(fileName, ios::app);

    // token结果，作为全局变量
    codeStream << "vector<pair<string, string>> tokens;" << endl << endl;
    // 分析函数
    codeStream << "int run(string input) {" << endl;
    codeStream << "    int currentState = 0;" << endl;
    // token起点
    codeStream << "    int start = 0;" << endl;
    codeStream << "    int length = input.length();" << endl;
    codeStream << "    for (int i = 0; i < length; i++) {" << endl;
    codeStream << "        char c = input[i];" << endl;
    codeStream << "        string symbol(1, input[i]);" << endl;
    // token第一位不能是空格或换行符
    codeStream << "        if(currentState == 0 && (c == ' ' || c == '\\n')) {" << endl;
    codeStream << "            start = i + 1;" << endl; // 同步移动token起点
    codeStream << "            continue;" << endl;
    codeStream << "        }" << endl;
    // 当前状态的转移
    codeStream << "        switch (currentState) {" << endl;
    for (const auto& node : mainReDFA) {
        codeStream << "            case " << node.first << ":" << endl;
        if (mainReEnds.count(node.first)) {
            // 到达终态，但是为了满足最长匹配原则，看是否能到达下一个状态
            // 遇到可以到下一个状态的字符：继续匹配，直到遇到错误的字符，这样可以保证最长匹配
            codeStream << "                if (false) ;" << endl;
            for (const auto& toNodeMap : node.second) {
                if (toNodeMap.first == "\n") codeStream << R"(                else if (symbol == "\n") )";
                else if (DFAsAndEndsOfAuxiliary.count(toNodeMap.first)) {
                    // 是不带_的正则名，则调用函数来判断是否符合该正则
                    codeStream << "                else if (" << toNodeMap.first << "(symbol)) ";
                }
                else codeStream << "                else if (symbol == \"" << toNodeMap.first << "\") ";
                codeStream << "currentState = " << toNodeMap.second << ";" << endl;
            }
            codeStream << "                else {" << endl;
            // 遇到错误的字符：说明到此之前的字符可以作为一个token，即此时已经是最长匹配token，取出token
            codeStream << "                        string token = input.substr(start, i - start);" << endl;
            // 接着遍历函数判断token类型
            codeStream << "                        string tokenType;" << endl;
            codeStream << "                        if (false) ;" << endl;
            for (const auto& DFAAndEnds : DFAsAndEndsOfTokenType) {
                codeStream << "                        else if (" << DFAAndEnds.first << "(token) != \"" << ILLEGAL <<
                    "\") tokenType = \"" << DFAAndEnds.first << "\";" << endl;
            }
            // 类型错误
            codeStream << "                        else {" << endl;
            codeStream << R"(                            cout << "No tokenType for token \"" << token << "\"" << endl;)"
                << endl;
            codeStream << "                            return 1;" << endl;
            codeStream << "                        }" << endl;
            // token里有换行就改为空格（比如说多行注释），否则tokens.txt文件格式就错了
            codeStream << "                        replace(token.begin(),token.end(),'\\n',' ');" << endl;
            // 保存token
            codeStream << "                        tokens.emplace_back(tokenType, token);" << endl;
            // 重新从起点开始匹配
            codeStream << "                        currentState = 0;" << endl;
            // 新的token字符串起点
            codeStream << "                        start = i;" << endl;
            // i后面要++，先回退一位（回退当前匹配错误的这一位，参与下个token的匹配）
            codeStream << "                        i--;" << endl;
            codeStream << "                }" << endl;
            codeStream << "                break;" << endl;
        }
        else {
            // 非终态，去往下一个状态
            codeStream << "                if (false) ;" << endl;
            for (const auto& toNodeMap : node.second) {
                if (toNodeMap.first == "\n") codeStream << R"(                else if (symbol == "\n") )";
                else if (DFAsAndEndsOfAuxiliary.count(toNodeMap.first)) {
                    // 是不带_的正则名，则调用函数来判断是否符合该正则
                    codeStream << "                else if (" << toNodeMap.first << "(symbol)) ";
                }
                else codeStream << "                else if (symbol == \"" << toNodeMap.first << "\") ";
                codeStream << "currentState = " << toNodeMap.second << ";" << endl;
            }
            codeStream << "                else {" << endl;
            // 遇到错误的字符直接就错误
            codeStream <<
                R"(                        cout << "Error: Invalid input character '" << c << "' at index "<< i << endl;)"
                << endl;
            codeStream << "                        return 1;" << endl;
            codeStream << "                }" << endl;
            codeStream << "                break;" << endl;
        }
    }
    codeStream << "        }" << endl;
    codeStream << "    }" << endl;
    // 最后跳出for循环时，最后一个字符串token没有保存，需要手动保存
    codeStream << "    string token = input.substr(start, input.length());" << endl;
    // 判断是否为空，不为空则是一个token
    codeStream << "    if(token != \"\") {" << endl;
    // 判断token类型
    codeStream << "        string tokenType;" << endl;
    codeStream << "        if (false) ;" << endl;
    for (const auto& DFAAndEnds : DFAsAndEndsOfTokenType) {
        codeStream << "        else if (" << DFAAndEnds.first << "(token) != \"" << ILLEGAL << "\") tokenType = \""
            << DFAAndEnds.first << "\";" << endl;
    }
    // 类型错误
    codeStream << "        else {" << endl;
    codeStream << R"(            cout << "No tokenType for token \"" << token << "\"" << endl;)" << endl;
    codeStream << "            return 1;" << endl;
    codeStream << "        }" << endl;
    // token里有换行就改为空格（比如说多行注释），否则tokens.txt文件格式就错了
    codeStream << "        replace(token.begin(),token.end(),'\\n',' ');" << endl;
    // 保存token
    codeStream << "        tokens.emplace_back(tokenType, token);" << endl;
    codeStream << "    }" << endl;
    // 分词成功
    codeStream << "    return 0;" << endl;
    codeStream << "}" << endl << endl;

    // main函数
    codeStream << "int main(int argc,char *argv[]) {" << endl;
    codeStream << "    string filePath(argv[1]);" << endl; // 代码内容所在路径
    // 读取内容，存入input
    codeStream << "    ifstream inputFile(filePath);" << endl;
    codeStream << "    string input;" << endl;
    codeStream << "    if(inputFile.is_open()) {" << endl;
    codeStream << "        stringstream buffer;" << endl;
    codeStream << "        buffer << inputFile.rdbuf();" << endl;
    codeStream << "        input = buffer.str();" << endl;
    codeStream << "    }" << endl;
    codeStream << "    else {" << endl;
    codeStream << "        cout << \"Unable to open file: \" << filePath << endl;" << endl;
    codeStream << "        return 1;" << endl;
    codeStream << "    }" << endl;
    // 记录是否分词成功，即使有错误的分词也把前面成功的分词保存
    codeStream << "    int returnStatus = run(input);" << endl;
    // 分词成功，把所有分好的词放到文件里
    codeStream << R"(    ofstream file("tokens.txt");)" << endl;
    codeStream << "    if (file.is_open()) {" << endl;
    codeStream << "        for(auto s: tokens) {" << endl;
    codeStream << "            file << s.first << \" \" << s.second << endl;" << endl;
    codeStream << "        }" << endl;
    codeStream << "        file.close();" << endl;
    codeStream << "    }" << endl;
    codeStream << "    else {" << endl;
    codeStream << R"(        cout << "Failed to save tokens to file. Unable to open file: tokens.txt" << endl;)" <<
        endl;
    codeStream << "        return 1;" << endl;
    codeStream << "    }" << endl;

    codeStream << "    return returnStatus;" << endl;
    codeStream << "}" << endl;

    // 写入cpp文件
    if (file.is_open()) {
        file << codeStream.str();
        file.close();
        cout << "Main function generated successfully." << endl;
    }
    else {
        cerr << "Failed to generate cpp file. Unable to open file: " << fileName << endl;
    }
    cout << endl;
}
